﻿using DotNetCommon.Data;
using DotNetCommon.Extensions;
using DotNetCommon.Validate.Annotations;
using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon.Validate;

/// <summary>
/// 模型校验帮助类
/// </summary>
public static class VValid
{
    #region validate attributes
    private static void ValidateByAttribute(Type type, object model, ValidateContext<object> parent)
    {
        if (type.IsSimpleType()) throw new Exception($"无法直接对 {type.GetClassFullName()} 进行校验!");
        if (type.IsAssignableTo(typeof(IDictionary)))
        {
            //字典
            var reflect = type.GetClassGenericFullName();
            var valueType = reflect.GenericTypes[1].type;
            if (valueType.IsSimpleType()) return;

            var dic = model as IDictionary;
            var enumerator = dic.GetEnumerator();
            while (enumerator.MoveNext())
            {
                var entry = enumerator.Entry;
                var newCtx = new ValidateContext<object>(model, entry.Key.ToString(), parent);
                ValidateByAttribute(valueType, entry.Value, newCtx);
            }
        }
        else if (type.IsAssignableTo(typeof(IEnumerable)))
        {
            //集合
            var reflect = type.GetClassGenericFullName();
            var valueType = reflect.GenericTypes[0].type;
            if (valueType.IsSimpleType()) return;

            var list = model as IEnumerable;
            var enumerator = list.GetEnumerator();
            var idx = -1;
            foreach (var val in list)
            {
                idx++;
                var newCtx = new ValidateContext<object>(val, $"[{idx}]", parent);
                ValidateByAttribute(valueType, val, newCtx);
            }
        }
        else if (type.IsArray)
        {
            //数组
            var reflect = type.GetClassGenericFullName();
            var valueType = reflect.GenericTypes[0].type;
            if (valueType.IsSimpleType()) return;

            var list = model as IEnumerable;
            var enumerator = list.GetEnumerator();
            var idx = -1;
            foreach (var val in list)
            {
                idx++;
                var newCtx = new ValidateContext<object>(val, $"[{idx}]", parent);
                ValidateByAttribute(valueType, val, newCtx);
            }
        }
        else
        {
            //poco
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                var val = props[i].GetValue(model);
                var newCtx = new ValidateContext<object>(val, VValid.AnalysisMemberName(type, prop.Name), parent);
                var attrs = prop.GetCustomAttributes();
                foreach (var attr in attrs)
                {
                    if (attr is IValidateAction validateAction)
                    {
                        validateAction.Action(newCtx);
                    }
                }
                if (!prop.PropertyType.IsSimpleType() && val != null)
                {
                    ValidateByAttribute(prop.PropertyType, val, newCtx);
                }
            }
        }
    }
    #endregion

    /// <summary>
    /// 使用给定的校验逻辑校验对象,提取出校验失败提示
    /// </summary>
    public static string ValidMessage<T>(T dto, Action<IValidateContext<T>> func = null)
    {
        ValidateContext root = null;
        if (func != null)
        {
            try
            {
                //自定义逻辑
                var ctx = new ValidateContext<T>(dto, "$");
                root = ctx;
                func(ctx);
            }
            catch (ValidateException) { }
        }
        else
        {
            //根据注解校验
            var root2 = new ValidateContext<object>(dto, "$");
            root = root2;
            ValidateByAttribute(typeof(T), dto, root2);
        }

        //组装结果(含子节点)
        var msg = FetchMessage(root);
        return msg?.TrimEnd('\n', '\r');
    }

    /// <summary>
    /// 使用给定的校验逻辑校验对象,提取出校验失败提示
    /// </summary>
    public static async Task<string> ValidMessageAsync<T>(T dto, Func<IValidateContext<T>, CancellationToken, Task> func, CancellationToken cancellationToken = default)
    {
        //肯定不是根据注解校验
        AssertUtil.NotNull(func);
        ValidateContext root = null;
        try
        {
            //自定义逻辑
            var ctx = new ValidateContext<T>(dto, "$");
            root = ctx;
            await func(ctx, cancellationToken);
        }
        catch (ValidateException) { }

        //组装结果(含子节点)
        var msg = FetchMessage(root);
        return msg?.TrimEnd('\n', '\r');
    }

    /// <summary>
    /// 使用给定的校验逻辑校验对象,返回结果响应对象
    /// </summary>
    public static Result ValidResult<T>(T dto, Action<IValidateContext<T>> func = null, int? errorCode = 0)
    {
        var msg = ValidMessage(dto, func);
        if (msg.IsNullOrWhiteSpace()) return Result.Ok();
        return Result.NotOk(msg, errorCode, null, null);
    }

    /// <summary>
    /// 使用给定的校验逻辑校验对象,返回结果响应对象
    /// </summary>
    public static async Task<Result> ValidResultAsync<T>(T dto, Func<IValidateContext<T>, CancellationToken, Task> func, int? errorCode = 0, CancellationToken cancellationToken = default)
    {
        var msg = await ValidMessageAsync(dto, func, cancellationToken);
        if (msg.IsNullOrWhiteSpace()) return Result.Ok();
        return Result.NotOk(msg, errorCode, null, null);
    }

    /// <summary>
    /// 使用给定的校验逻辑校验对象,返回结果响应对象
    /// </summary>
    public static void ValidThrow<T>(T dto, Action<IValidateContext<T>> func = null, int? errorCode = 0)
    {
        var msg = ValidMessage(dto, func);
        if (msg.IsNullOrWhiteSpace()) return;
        ResultException.Throw(msg, errorCode);
    }

    /// <summary>
    /// 使用给定的校验逻辑校验对象,返回结果响应对象
    /// </summary>
    public static async Task ValidThrowAsync<T>(T dto, Func<IValidateContext<T>, CancellationToken, Task> func, int? errorCode = 0, CancellationToken cancellationToken = default)
    {
        var msg = await ValidMessageAsync(dto, func, cancellationToken);
        if (msg.IsNullOrWhiteSpace()) return;
        ResultException.Throw(msg, errorCode);
    }

    internal static string FetchMessage(IValidateContext ctx)
    {
        var sb = new StringBuilder();
        if (ctx.ErrorMessage.Count > 0)
        {
            foreach (var item in ctx.ErrorMessage)
            {
                sb.Append(item.error + "\r\n");
            }
        }
        var msg = sb.ToString();
        sb.Clear();
        return msg;
    }

    #region region test AnalysisMember
    //AnalysisMember2("i=>i.Name");
    //AnalysisMember2("i=>i. Name");
    //AnalysisMember2("i=>i . Name");
    //AnalysisMember2("i=>i . Name  ");
    //AnalysisMember2("i=>i.Name ?? \"\"");
    //AnalysisMember2("i=>i[\"Name\"]");
    //AnalysisMember2("i=>i[ \"Name\" ]");
    //Console.WriteLine("ok");
    //void AnalysisMember2(string lambdaString)
    //{
    //    Console.WriteLine(lambdaString + "=========" + AnalysisMember(lambdaString));
    //} 

    internal static string AnalysisMember(string lambdaString)
    {
        var idx = lambdaString.IndexOf("=>");
        if (idx < 0) return lambdaString;//无法解析,比如传入的是变量: callMethod(myFunc)
                                         //i => i.Name
        var para = lambdaString.AsSpan()[..idx].Trim().ToString();//参数: 如: 'i' 或 'item'
        var str = lambdaString.AsSpan()[(idx + 2)..].Trim().ToString();//如: 'i=>i.Name' 去掉前面剩下: 'i.Name'

        //最简单的形式
        if (str.StartsWith(para + ".") && !str.Contains('?') && !str.Contains('[') && !str.Contains('('))
        {
            //i.Name
            return str[(para.Length + 1)..].Trim();
        }
        //使用正则匹配
        //i.Name
        var match = new Regex($"{para}\\s{{0,}}\\.\\s{{0,}}(\\S+)[\\s|\\?|\\(]{{0,}}").Match(str);//i.Name or i.Name??"ko"
        if (!match.Success) match = new Regex($"{para}\\s{{0,}}\\[\\s{{0,}}\\\"(\\S+)\\\"\\s{{0,}}\\][\\s|\\?|\\(]{{0,}}").Match(str);//i["Name"]
        if (match.Success)
        {
            return match.Groups[match.Groups.Count - 1].Value;
        }
        return str;
    }
    internal static string AnalysisMemberName(Type type, string propName)
    {
        if (type.TryGetInstanceProperty(propName, out var prop))
        {
            var attrs = prop.GetCustomAttributes(typeof(VDisplayNameAttribute), true);
            if (attrs.Length > 0)
            {
                var attr = attrs[0] as VDisplayNameAttribute;
                if (attr.Name.IsNotNullOrWhiteSpace())
                {
                    propName = attr.Name;
                }
            }
            else
            {
                attrs = prop.GetCustomAttributes(typeof(DisplayNameAttribute), true);
                if (attrs.Length > 0)
                {
                    var attr = attrs[0] as DisplayNameAttribute;
                    if (attr.DisplayName.IsNotNullOrWhiteSpace())
                    {
                        propName = attr.DisplayName;
                    }
                }
            }
        }
        return propName;
    }
    #endregion
}
